package com.kwdb.importtool.util;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * 文件操作工具类
 * 提供文件读写、复制、删除等常用操作
 */
@Slf4j
public class FileUtils {

    // 缓冲区大小
    private static final int BUFFER_SIZE = 8192;

    // 支持的SQL文件扩展名
    private static final String[] SQL_FILE_EXTENSIONS = {".sql", ".txt", ".dump"};

    private FileUtils() {
        // 工具类，防止实例化
    }

    /**
     * 读取文件内容为字符串
     */
    public static String readFileToString(File file) throws IOException {
        return readFileToString(file, StandardCharsets.UTF_8);
    }

    /**
     * 读取文件内容为字符串
     */
    public static String readFileToString(File file, Charset charset) throws IOException {
        if (file == null || !file.exists()) {
            throw new FileNotFoundException("文件不存在: " + (file != null ? file.getPath() : "null"));
        }

        if (!file.isFile()) {
            throw new IOException("路径不是文件: " + file.getPath());
        }

        if (!file.canRead()) {
            throw new IOException("文件不可读: " + file.getPath());
        }

        try (FileInputStream fis = new FileInputStream(file);
             InputStreamReader isr = new InputStreamReader(fis, charset);
             BufferedReader reader = new BufferedReader(isr)) {

            StringBuilder content = new StringBuilder();
            char[] buffer = new char[BUFFER_SIZE];
            int charsRead;

            while ((charsRead = reader.read(buffer)) != -1) {
                content.append(buffer, 0, charsRead);
            }

            log.debug("成功读取文件: {} (大小: {} 字符)", file.getPath(), content.length());
            return content.toString();
        }
    }

    /**
     * 将字符串写入文件
     */
    public static void writeStringToFile(File file, String content) throws IOException {
        writeStringToFile(file, content, StandardCharsets.UTF_8);
    }

    /**
     * 将字符串写入文件
     */
    public static void writeStringToFile(File file, String content, Charset charset) throws IOException {
        if (file == null) {
            throw new IllegalArgumentException("文件不能为null");
        }

        // 确保父目录存在
        File parentDir = file.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            if (!parentDir.mkdirs()) {
                throw new IOException("无法创建目录: " + parentDir.getPath());
            }
        }

        try (FileOutputStream fos = new FileOutputStream(file);
             OutputStreamWriter osw = new OutputStreamWriter(fos, charset);
             BufferedWriter writer = new BufferedWriter(osw)) {

            writer.write(content);
            writer.flush();

            log.debug("成功写入文件: {} (大小: {} 字符)", file.getPath(), content.length());
        }
    }

    /**
     * 复制文件
     */
    public static void copyFile(File source, File target) throws IOException {
        if (source == null || target == null) {
            throw new IllegalArgumentException("源文件或目标文件不能为null");
        }

        if (!source.exists()) {
            throw new FileNotFoundException("源文件不存在: " + source.getPath());
        }

        if (!source.isFile()) {
            throw new IOException("源路径不是文件: " + source.getPath());
        }

        // 确保目标目录存在
        File parentDir = target.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            if (!parentDir.mkdirs()) {
                throw new IOException("无法创建目录: " + parentDir.getPath());
            }
        }

        try (FileInputStream fis = new FileInputStream(source);
             FileOutputStream fos = new FileOutputStream(target)) {

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }

            log.debug("成功复制文件: {} -> {}", source.getPath(), target.getPath());
        }
    }

    /**
     * 复制目录
     */
    public static void copyDirectory(File sourceDir, File targetDir) throws IOException {
        if (sourceDir == null || targetDir == null) {
            throw new IllegalArgumentException("源目录或目标目录不能为null");
        }

        if (!sourceDir.exists()) {
            throw new FileNotFoundException("源目录不存在: " + sourceDir.getPath());
        }

        if (!sourceDir.isDirectory()) {
            throw new IOException("源路径不是目录: " + sourceDir.getPath());
        }

        // 创建目标目录
        if (!targetDir.exists() && !targetDir.mkdirs()) {
            throw new IOException("无法创建目标目录: " + targetDir.getPath());
        }

        // 遍历源目录
        File[] files = sourceDir.listFiles();
        if (files != null) {
            for (File file : files) {
                File targetFile = new File(targetDir, file.getName());

                if (file.isDirectory()) {
                    copyDirectory(file, targetFile);
                } else {
                    copyFile(file, targetFile);
                }
            }
        }

        log.debug("成功复制目录: {} -> {}", sourceDir.getPath(), targetDir.getPath());
    }

    /**
     * 删除文件或目录
     */
    public static boolean delete(File file) {
        if (file == null || !file.exists()) {
            return true;
        }

        if (file.isDirectory()) {
            return deleteDirectory(file);
        } else {
            return deleteFile(file);
        }
    }

    /**
     * 删除文件
     */
    public static boolean deleteFile(File file) {
        if (file == null || !file.exists()) {
            return true;
        }

        boolean success = file.delete();
        if (success) {
            log.debug("成功删除文件: {}", file.getPath());
        } else {
            log.warn("删除文件失败: {}", file.getPath());
        }

        return success;
    }

    /**
     * 删除目录（递归删除所有内容）
     */
    public static boolean deleteDirectory(File directory) {
        if (directory == null || !directory.exists()) {
            return true;
        }

        if (!directory.isDirectory()) {
            return deleteFile(directory);
        }

        // 递归删除子文件和子目录
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    deleteFile(file);
                }
            }
        }

        // 删除空目录
        boolean success = directory.delete();
        if (success) {
            log.debug("成功删除目录: {}", directory.getPath());
        } else {
            log.warn("删除目录失败: {}", directory.getPath());
        }

        return success;
    }

    /**
     * 创建目录（包括父目录）
     */
    public static boolean createDirectory(File directory) {
        if (directory == null) {
            return false;
        }

        if (directory.exists()) {
            return directory.isDirectory();
        }

        boolean success = directory.mkdirs();
        if (success) {
            log.debug("成功创建目录: {}", directory.getPath());
        } else {
            log.warn("创建目录失败: {}", directory.getPath());
        }

        return success;
    }

    /**
     * 获取文件大小（人类可读格式）
     */
    public static String getHumanReadableSize(File file) {
        if (file == null || !file.exists()) {
            return "0 B";
        }

        long size = file.length();
        return formatFileSize(size);
    }

    /**
     * 格式化文件大小
     */
    public static String formatFileSize(long size) {
        if (size < 1024) {
            return size + " B";
        } else if (size < 1024 * 1024) {
            return String.format("%.1f KB", size / 1024.0);
        } else if (size < 1024 * 1024 * 1024) {
            return String.format("%.1f MB", size / (1024.0 * 1024.0));
        } else {
            return String.format("%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
        }
    }

    /**
     * 获取文件扩展名
     */
    public static String getFileExtension(File file) {
        if (file == null) {
            return "";
        }

        String fileName = file.getName();
        int lastDotIndex = fileName.lastIndexOf('.');

        if (lastDotIndex > 0 && lastDotIndex < fileName.length() - 1) {
            return fileName.substring(lastDotIndex + 1).toLowerCase();
        }

        return "";
    }

    /**
     * 检查文件是否为SQL文件
     */
    public static boolean isSQLFile(File file) {
        if (file == null || !file.isFile()) {
            return false;
        }

        String extension = getFileExtension(file);
        for (String sqlExt : SQL_FILE_EXTENSIONS) {
            if (sqlExt.substring(1).equalsIgnoreCase(extension)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 获取文件MD5哈希值
     */
    public static String getFileMD5(File file) throws IOException {
        if (file == null || !file.exists()) {
            throw new FileNotFoundException("文件不存在");
        }

        try (FileInputStream fis = new FileInputStream(file)) {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }

            byte[] hashBytes = digest.digest();
            StringBuilder hexString = new StringBuilder();

            for (byte b : hashBytes) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }

            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new IOException("MD5算法不可用", e);
        }
    }

    /**
     * 读取文件的所有行
     */
    public static List<String> readLines(File file) throws IOException {
        return readLines(file, StandardCharsets.UTF_8);
    }

    /**
     * 读取文件的所有行
     */
    public static List<String> readLines(File file, Charset charset) throws IOException {
        List<String> lines = new ArrayList<>();

        if (file == null || !file.exists()) {
            return lines;
        }

        try (FileInputStream fis = new FileInputStream(file);
             InputStreamReader isr = new InputStreamReader(fis, charset);
             BufferedReader reader = new BufferedReader(isr)) {

            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        }

        return lines;
    }

    /**
     * 将行列表写入文件
     */
    public static void writeLines(File file, List<String> lines) throws IOException {
        writeLines(file, lines, StandardCharsets.UTF_8);
    }

    /**
     * 将行列表写入文件
     */
    public static void writeLines(File file, List<String> lines, Charset charset) throws IOException {
        if (file == null || lines == null) {
            return;
        }

        try (FileOutputStream fos = new FileOutputStream(file);
             OutputStreamWriter osw = new OutputStreamWriter(fos, charset);
             BufferedWriter writer = new BufferedWriter(osw)) {

            for (String line : lines) {
                writer.write(line);
                writer.newLine();
            }
        }
    }

    /**
     * 创建临时文件
     */
    public static File createTempFile(String prefix, String suffix) throws IOException {
        return createTempFile(prefix, suffix, null);
    }

    /**
     * 创建临时文件
     */
    public static File createTempFile(String prefix, String suffix, File directory) throws IOException {
        if (prefix == null) {
            prefix = "temp";
        }

        if (suffix == null) {
            suffix = ".tmp";
        }

        if (directory == null) {
            directory = new File(System.getProperty("java.io.tmpdir"));
        }

        // 确保目录存在
        if (!directory.exists() && !directory.mkdirs()) {
            throw new IOException("无法创建临时目录: " + directory.getPath());
        }

        File tempFile = File.createTempFile(prefix, suffix, directory);
        tempFile.deleteOnExit();

        log.debug("创建临时文件: {}", tempFile.getPath());
        return tempFile;
    }

    /**
     * 创建临时目录
     */
    public static File createTempDirectory(String prefix) throws IOException {
        if (prefix == null) {
            prefix = "temp";
        }

        Path tempDirPath = Files.createTempDirectory(prefix);
        File tempDir = tempDirPath.toFile();
        tempDir.deleteOnExit();

        log.debug("创建临时目录: {}", tempDir.getPath());
        return tempDir;
    }

    /**
     * 备份文件
     */
    public static File backupFile(File originalFile) throws IOException {
        return backupFile(originalFile, null);
    }

    /**
     * 备份文件
     */
    public static File backupFile(File originalFile, File backupDir) throws IOException {
        if (originalFile == null || !originalFile.exists()) {
            throw new FileNotFoundException("原始文件不存在");
        }

        if (backupDir == null) {
            backupDir = originalFile.getParentFile();
        }

        // 创建备份目录
        if (!backupDir.exists() && !backupDir.mkdirs()) {
            throw new IOException("无法创建备份目录: " + backupDir.getPath());
        }

        // 生成备份文件名
        String timestamp = String.valueOf(System.currentTimeMillis());
        String backupFileName = originalFile.getName() + ".backup." + timestamp;
        File backupFile = new File(backupDir, backupFileName);

        // 复制文件
        copyFile(originalFile, backupFile);

        log.info("文件备份成功: {} -> {}", originalFile.getPath(), backupFile.getPath());
        return backupFile;
    }

    /**
     * 压缩文件
     */
    public static File zipFile(File sourceFile, File zipFile) throws IOException {
        if (sourceFile == null || !sourceFile.exists()) {
            throw new FileNotFoundException("源文件不存在");
        }

        if (zipFile == null) {
            String zipName = sourceFile.getName() + ".zip";
            zipFile = new File(sourceFile.getParent(), zipName);
        }

        try (FileOutputStream fos = new FileOutputStream(zipFile);
             ZipOutputStream zos = new ZipOutputStream(fos)) {

            addFileToZip(sourceFile, sourceFile.getName(), zos);
        }

        log.debug("文件压缩成功: {} -> {}", sourceFile.getPath(), zipFile.getPath());
        return zipFile;
    }

    /**
     * 添加文件到ZIP输出流
     */
    private static void addFileToZip(File file, String entryName, ZipOutputStream zos) throws IOException {
        if (file.isDirectory()) {
            // 添加目录条目
            if (!entryName.endsWith("/")) {
                entryName += "/";
            }

            ZipEntry entry = new ZipEntry(entryName);
            zos.putNextEntry(entry);
            zos.closeEntry();

            // 递归添加子文件和子目录
            File[] children = file.listFiles();
            if (children != null) {
                for (File child : children) {
                    addFileToZip(child, entryName + child.getName(), zos);
                }
            }
        } else {
            // 添加文件条目
            ZipEntry entry = new ZipEntry(entryName);
            zos.putNextEntry(entry);

            try (FileInputStream fis = new FileInputStream(file)) {
                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead;

                while ((bytesRead = fis.read(buffer)) != -1) {
                    zos.write(buffer, 0, bytesRead);
                }
            }

            zos.closeEntry();
        }
    }

    /**
     * 解压ZIP文件
     */
    public static void unzipFile(File zipFile, File destDir) throws IOException {
        if (zipFile == null || !zipFile.exists()) {
            throw new FileNotFoundException("ZIP文件不存在");
        }

        if (destDir == null) {
            destDir = zipFile.getParentFile();
        }

        // 创建目标目录
        if (!destDir.exists() && !destDir.mkdirs()) {
            throw new IOException("无法创建目标目录: " + destDir.getPath());
        }

        try (FileInputStream fis = new FileInputStream(zipFile);
             ZipInputStream zis = new ZipInputStream(fis)) {

            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                File entryFile = new File(destDir, entry.getName());

                // 防止ZIP滑动攻击
                if (!entryFile.getCanonicalPath().startsWith(destDir.getCanonicalPath())) {
                    throw new IOException("无效的ZIP条目: " + entry.getName());
                }

                if (entry.isDirectory()) {
                    if (!entryFile.exists() && !entryFile.mkdirs()) {
                        throw new IOException("无法创建目录: " + entryFile.getPath());
                    }
                } else {
                    // 确保父目录存在
                    File parentDir = entryFile.getParentFile();
                    if (parentDir != null && !parentDir.exists()) {
                        if (!parentDir.mkdirs()) {
                            throw new IOException("无法创建目录: " + parentDir.getPath());
                        }
                    }

                    // 解压文件
                    try (FileOutputStream fos = new FileOutputStream(entryFile)) {
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead;

                        while ((bytesRead = zis.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                    }
                }

                zis.closeEntry();
            }
        }

        log.debug("文件解压成功: {} -> {}", zipFile.getPath(), destDir.getPath());
    }

    /**
     * 获取目录大小（递归计算）
     */
    public static long getDirectorySize(File directory) {
        if (directory == null || !directory.exists() || !directory.isDirectory()) {
            return 0;
        }

        final long[] size = {0};

        try {
            Files.walkFileTree(directory.toPath(), new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    size[0] += attrs.size();
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    log.warn("无法访问文件: {}", file, exc);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            log.warn("计算目录大小时出错: {}", directory.getPath(), e);
        }

        return size[0];
    }

    /**
     * 检查磁盘空间是否足够
     */
    public static boolean hasEnoughSpace(File file, long requiredSize) {
        if (file == null) {
            return false;
        }

        File diskPartition = file.getParentFile();
        if (diskPartition == null) {
            diskPartition = new File("/");
        }

        long freeSpace = diskPartition.getFreeSpace();
        return freeSpace >= requiredSize;
    }

    /**
     * 文件信息类
     */
    public static class FileInfo {
        private final String fileName;
        private final String filePath;
        private final long fileSize;
        private final long lastModified;
        private final boolean isDirectory;

        public FileInfo(File file) {
            this.fileName = file.getName();
            this.filePath = file.getAbsolutePath();
            this.fileSize = file.length();
            this.lastModified = file.lastModified();
            this.isDirectory = file.isDirectory();
        }

        // Getters
        public String getFileName() { return fileName; }
        public String getFilePath() { return filePath; }
        public long getFileSize() { return fileSize; }
        public long getLastModified() { return lastModified; }
        public boolean isDirectory() { return isDirectory; }

        public String getFormattedSize() {
            return formatFileSize(fileSize);
        }

        public String getFormattedLastModified() {
            return new java.util.Date(lastModified).toString();
        }

        @Override
        public String toString() {
            return String.format("FileInfo{name='%s', size=%s, modified=%s, isDir=%s}",
                    fileName, getFormattedSize(), getFormattedLastModified(), isDirectory);
        }
    }

    /**
     * 获取文件信息
     */
    public static FileInfo getFileInfo(File file) {
        if (file == null || !file.exists()) {
            return null;
        }
        return new FileInfo(file);
    }
}